﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Collections;
using System.Net.Sockets;
using System.Net;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using Package;
using ClientEndpoint;
using System.Threading;

namespace NetworkATMForm
{
    public partial class Form1 : Form
    {
        List<string> config = new List<string>();
        List<string> pole = new List<string>();

        List<FIBEntry> fib = new List<FIBEntry>();
        string switchfile;

        public string nazwa = "";
        ArrayList ports = new ArrayList();
        int portTCPAgentReceive = 0;
        int portTCPAgentSend = 0;
        IPAddress CloudAddress = IPAddress.Parse("127.0.0.1");
        int portTCPCloud = 50001;
        string[] args = Environment.GetCommandLineArgs();
        ATMCell.AtmCell receivedCell;

        int receivedPort;
        int receivedVpi;
        int receivedVci;
        int targetPort;
        int targetVpi;
        int targetVci;
        
        private ComParser deserializer = new ComParser();
        private string agentResponse;
        private string agentPrint;
        private NetnodeAgent agent;
        private System.Windows.Forms.Timer ka = null;

        public Form1()
            {
            InitializeComponent();
            InitializeCommands();
            }
        private void InitializeCommands()
            {
            deserializer.AddFormat(">[portwe]@[vpiwe]@[vciwe]#[portwy]@[vpiwy]@[vciwy]", AddForwarding);
            }
        public string GetFromAgent(int inPort, int inVpi, int inVci)
        {
            foreach (FIBEntry fe in fib)
            {
                if (fe.Match(inPort, inVpi, inVci))
                {
                    return fe.grspName;
                }
            }
            return "-,-,-,-,-,-";
        }
        public string AddFromAgent(string add)
            {
                agentResponse = ""; agentPrint = "";
                deserializer.Execute(add);
                RefreshForwards();
                Print(agentPrint);
                return agentResponse;
            }
        private void AddForwarding(ArgList args)
            {
            int inPort, inVpi, inVci, outPort, outVpi, outVci;
            inPort = Int32.Parse(args.Get("portwe"));
            inVpi = Int32.Parse(args.Get("vpiwe"));
            inVci = Int32.Parse(args.Get("vciwe"));
            outPort = Int32.Parse(args.Get("portwy"));
            outVpi = Int32.Parse(args.Get("vpiwy"));
            outVci = Int32.Parse(args.Get("vciwy"));

            //od razu sprawdza przy dodawaniu czy porty FIBa są właściwe
            if (ports.IndexOf(inPort) < 4 && ports.IndexOf(inPort) > -1 && ports.IndexOf(outPort) >= 4)
            {
                foreach (FIBEntry fe in fib)
                {
                    if (fe.Match(inPort, inVpi, inVci))
                    {
                        agentPrint = "Nadpisano przekierowanie z portu " + inPort + ", VPI " + inVpi + ", VCI " + inVci + " na polecenie zarządcy!";
                        agentResponse = "OK,OVERWRITTEN";
                        fe.Reforward(outPort, outVpi, outVci);
                        return;
                    }
                }
                agentResponse = "OK,ADDED";
                agentPrint = "Dodano przekierowanie z portu " + inPort + ", VPI " + inVpi + ", VCI " + inVci + " na polecenie zarządcy!";
                fib.Add(new FIBEntry(inPort, inVpi, inVci, outPort, outVpi, outVci));
            }
            else
                {
                agentResponse = "ERROR,BADPORTS";
                agentPrint = "Błędne polecenie przekierowania!";
                }
            }
        public string RemoveFromAgent(int inPort, int inVpi, int inVci)
            {
            agentResponse = "ERROR,NOENTRY";
            List<FIBEntry> rem = new List<FIBEntry>();
            Print("Usuwanie połączeń związanych z portem wejściowym "+inPort+", VPI "+inVpi+", VCI "+inVci);
            foreach(FIBEntry fe in fib)
                {
                if (fe.Match(inPort, inVpi, inVci))
                    {
                        rem.Add(fe);
                    }
                }
            foreach (FIBEntry fe in rem)
                {
                Print("Na polecenie zarządcy usunięto połączenie: "+fe.DispName);
                fib.Remove(fe);
                agentResponse = "OK,REMOVED";
                }
            if (agentResponse == "ERROR,NOENTRY")
                Print("Nie znaleziono takich połączeń!");
            else
                RefreshForwards();
            return agentResponse;
            }
        private void RefreshForwards()
            {
            listBox2.DataSource = null;
            listBox2.DataSource = fib;
            listBox2.DisplayMember = "DispName";
            }

        public void Print(string toPrint)
            {
            listBox1.Items.Add(toPrint);
            }
        private void FIBSerialize()
            {
                StreamWriter objWriter = new StreamWriter(switchfile);
                foreach (FIBEntry fe in fib)
                    {
                    objWriter.WriteLine(fe.ToString());                    
                    }
                objWriter.Close();
            }


        public void forwardVpci()
            {
            foreach (FIBEntry fe in fib)
                {
                if (receivedPort == fe.receivedPort && receivedVpi == fe.receivedVpi && receivedVci == fe.receivedVci)
                    {
                    targetPort = fe.targetPort;
                    targetVpi = fe.targetVpi;
                    targetVci = fe.targetVci;
                    break;
                    }
                }
            }
        private void AmAlive(object sender, EventArgs e)
            {
            agent.AmAlive();
            }

        private int getIntFromBitArray(BitArray bitArray)
        {
            int value = 0;

            for (int i = 0; i < bitArray.Count; i++)
            {
                if (bitArray[i])
                    value += Convert.ToInt16(Math.Pow(2, i));
            }

            return value;
        }



        private void cellhandler(Package.CellPackage package)
        {
            var cell = package.data as ATMCell.AtmCell;

            ATMCell.AtmCell.AtmHeader head = cell.header;
            receivedCell = cell;
            BitArray vpi = head.vpi;
            BitArray vci = head.vci;
            receivedPort = package.linkPort;
            receivedVci = getIntFromBitArray(vci);
            receivedVpi = getIntFromBitArray(vpi);
            forwardVpci();
            receivedCell.header.vpi = new BitArray(targetVpi);
            receivedCell.header.vci = new BitArray(targetVci);
            Print("Przekierowano pakiet ATM z portu "+receivedPort +", VPI "+ receivedVpi + ", VCI " + receivedVci + " na port "+targetPort+", VPI "+targetVpi+", VCI "+targetVci);
            passMessage(cell.data);
        }

        void passMessage(byte[] message)
        {
            var sender1 = Sender.getInstance();
            sender1.passMessage(message, targetPort, targetVpi, targetVci);
        }

        private void button1_Click_1(object sender, EventArgs e)
        {
            string Konfiguracja = "";
            StreamReader objReader = new StreamReader(args[1]); //args[1]
            while (Konfiguracja != null)
            {
                Konfiguracja = objReader.ReadLine();
                if (Konfiguracja != null)
                    config.Add(Konfiguracja);

            }
            objReader.Close();

            nazwa = config[0];

            //portTCPCloudReceive = Int32.Parse(config[1]);
            portTCPAgentReceive = Int32.Parse(config[1]);
            portTCPAgentSend = Int32.Parse(config[2]);

            //inicjalizacja agenta
            agent = new NetnodeAgent(nazwa, portTCPAgentReceive, portTCPAgentSend, this);
            ka = new System.Windows.Forms.Timer();
            ka.Tick += new EventHandler(AmAlive);
            ka.Interval = 5000;
            ka.Start();
            agent.AmAlive();

             for (int i = 3; i <11 ; i++)
                {
                ports.Add(Int32.Parse(config[i]));
                }

            config.Clear();
            Konfiguracja = "";
            switchfile = args[2];
            objReader = new StreamReader(switchfile); //args[1]
            while (Konfiguracja != null)
            {
                Konfiguracja = objReader.ReadLine();
                if (Konfiguracja != null)
                    config.Add(Konfiguracja);

            }
            objReader.Close();

            foreach (string s in config)
                {
                deserializer.Execute(s);
                }
            if (config.Count != fib.Count)
                {
                //FIBSerialize();
                }
            RefreshForwards();

            CloudConnect();
        }
        private void CloudConnect()
        {
            try
            {
                ClientProperties.getInstance(nazwa, ports, HelloPackage.NodeType.NNI);
                var cCend = CableCloudEndPoint.getInstance(CloudAddress, portTCPCloud, cellhandler);
                int liczba = cCend.mainPort;
                textBox1.Text = "" + liczba;
                var receiving = new Thread(cCend.startListeningForCells) { IsBackground = true };
                receiving.Start();


            }
            catch
            {
                MessageBox.Show("Chmura nie działa!!!");
            }
        }


        private void textBox1_TextChanged(object sender, EventArgs e)
        {

        }

    }

    public class FIBEntry
        {
        public int receivedPort { get; private set; }
        public int receivedVpi { get; private set; }
        public int receivedVci { get; private set; }
        public int targetPort { get; private set; }
        public int targetVpi { get; private set; }
        public int targetVci { get; private set; }
        public FIBEntry(int rPort, int rVpi, int rVci, int tPort, int tVpi, int tVci)
            {
            receivedPort = rPort;
            receivedVpi = rVpi;
            receivedVci = rVci;
            targetPort = tPort;
            targetVpi = tVpi;
            targetVci = tVci;
            }
        public override string ToString()
            {
                string ret=">";
                ret += receivedPort + "@" + receivedVpi + "@" + receivedVci + "#";
                ret += targetPort + "@" + targetVpi + "@" + targetVci;
                return ret;
            }
        public bool Match(int rPort, int rVpi, int rVci)
            {
            return (receivedPort == rPort && receivedVpi == rVpi && receivedVci == rVci);
            }
        public void Reforward(int tPort, int tVpi, int tVci)
            {
                targetPort = tPort;
                targetVpi = tVpi;
                targetVci = tVci;
            }
        public string DispName
            {
            get
                {
                return "p" + receivedPort + " " + receivedVpi + ":" + receivedVci + " => " + "p" + targetPort + " " + targetVpi + ":" + targetVci;
                }
            }
        public string grspName
            {
            get
                {
                return receivedPort + "," + receivedVpi + "," + receivedVci + "," + targetPort + "," + targetVpi + "," + targetVci;
                }
            }
        }
}
